package com.mall.rabbitmq.demo3;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.rabbitmq.client.Channel;
import com.mall.vo.DemoVO;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
@Component
//指定需要监听的队列
//@RabbitListener(queues = "item_queue")
public class MessageListener {

    /**
     * 在类上指定后 就不需要在方法中指定监听队列了
     * RabbitHandler 和 类上面RabbitListener 搭配使用
     * channel 频道对象 他提供了ack/nack方法（签收和拒绝签收）
     * Message 消息本生的封装的对象
     * String msg 消息的本身（）
     */
    //@RabbitHandler
    @RabbitListener(queues = "item_queue")
    public void msg(Message message, Channel channel, String msg){
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //路由名称
            String key =  message.getMessageProperties().getReceivedRoutingKey();
            //交换机名称
            String exchange =  message.getMessageProperties().getReceivedExchange();
            //队列名称
            String queue =  message.getMessageProperties().getConsumerQueue();
            //接收消息
            DemoVO vo = JSONUtil.toBean(msg, DemoVO.class);
            System.out.println("消息下标："+deliveryTag+";路由："+key+";交换机："+exchange+";队列："+queue+";消费者接收到的消息："+msg+";解析后："+ (StrUtil.isEmptyIfStr(vo)?"1":vo.getDestination()));
            //消息下标：4;路由：item.new;交换机：item_topic_exchange;队列：item_queue;消费者接收到的消息：{"destination":"1"};解析后：1
            //int i = 1 / 0;
            //签收消息
            // 参数1 指定的是消息的序号（快递号）
            // 参数2 指定是否需要批量的签收 如果是true,那就批量 如果是false 那就不批量
            //如果一直未签收 则会在MQ中处于待确认状态 在项目重启后进入死信队列中输出 待确认消息在MQ重启后消息丢失
            channel.basicAck(deliveryTag,false);
        } catch (Exception e) {
            try {
                System.out.println(deliveryTag);
                //如果出现异常，则拒绝消息 可以重回队列 也可以丢弃 可以根据业务场景来
                //方式一：可以批量处理y用：basicNack，传三个参数
                /**
                 * 参数3 标识是否重回队列
                 * true 是重回 如果重回队列的话，异常没有解决，就会进入死循环
                 * false 就是不重回:丢弃消息
                 * 如果存在死信队列 重回队列的消息在死循环中达到队列设置的超时时间后进入死信队列
                 * 不重回的消息会进入死信队列中
                  */
                channel.basicNack(deliveryTag,false,false);
                //方式二：不批量处理：basicReject，传两个参数，第二个参数是否批量
                //channel.basicReject(deliveryTag,false);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    /**
     * 监听死信队列的消息 简洁配置模式
     * @param message 接收到的消息体
     * @param channel 消息中心处理
     */
    @RabbitListener(queues = "xkp_queue")
    public void myListener1(Message message, Channel channel) throws IOException{
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            // 转换消息
            System.out.println(deliveryTag+"死信消息内容: " + new String(message.getBody()));
            // 手工签收
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            // 4. 拒绝签收
            // 参数3：true：重回队列，broker会重新发送该消息给消费端，false：不重回队列，消息会被丢弃
            channel.basicNack(deliveryTag,true,false);
        }
    }

    /**
     * 监听某个队列的消息 简单模式 单纯监听 由于在配置文件中开启手工签收消息 所以这个方法里面接收到的消息无法通知消息中心销毁
     * #设置监听端消息ACK确认模式为手动模式，默认自动确认接收消息，无论是否出异常
     * spring.rabbitmq.listener.simple.acknowledge-mode=manual
     * @param message 接收到的消息
     */
//    @RabbitListener(queues = "item_queue")
//    public void myListener1(String message){
//        System.out.println("消费者接收到的消息为：" + message);
//    }

    /**
     * 测试签收消息
     **/
//    @RabbitListener(bindings = @QueueBinding(
//            exchange = @Exchange(value = "item_topic_exchange",type = "topic"),
//            value = @Queue(value = "item_queue",durable = "true"),
//            key = "item.#"
//    ))
//    public void myListener1(Message message, Channel channel) throws IOException{
//        long deliveryTag = message.getMessageProperties().getDeliveryTag();
//        try {
//            if(deliveryTag%5==0){
//                System.out.println(1/0);
//            }
//            // 转换消息
//            System.out.println(deliveryTag+"消息内容: " + new String(message.getBody()));
//            // 手工签收
//            channel.basicAck(deliveryTag,false);
//
//        } catch (Exception e) {
//            // 4. 拒绝签收
//            // 参数3：true：重回队列，broker会重新发送该消息给消费端，false：不重回队列，消息会被丢弃
//            channel.basicNack(deliveryTag,true,false);
//        }
//    }
}
